package org.ws.httphelper.support.annotation;

import org.apache.commons.lang3.StringUtils;
import org.ws.httphelper.HttpHelper;
import org.ws.httphelper.builder.RequestTemplateBuilder;
import org.ws.httphelper.builder.annotation.Delete;
import org.ws.httphelper.builder.annotation.Get;
import org.ws.httphelper.builder.annotation.HttpClient;
import org.ws.httphelper.builder.annotation.HttpRequest;
import org.ws.httphelper.builder.annotation.Post;
import org.ws.httphelper.builder.annotation.Put;
import org.ws.httphelper.common.Constant;
import org.ws.httphelper.model.config.ClientConfig;
import org.ws.httphelper.model.config.RequestConfig;
import org.ws.httphelper.model.http.ResponseFuture;
import org.ws.httphelper.model.template.RequestTemplate;
import org.ws.httphelper.support.DefaultHttpHelper;
import org.ws.httphelper.template.DefaultClientConfig;
import org.ws.httphelper.template.DefaultRequestConfig;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.function.Consumer;
import java.util.regex.Pattern;

public class OperationMethod {

    private final HttpHelper httpHelper;
    private final Method method;
    private final RequestTemplate parentTemplate;
    private final HttpHelper parentHttpHelper;
    private static final Class<? extends Annotation> [] METHOD_ANNOTATION = new Class[]{HttpRequest.class,
        Get.class,Post.class,Put.class,Delete.class};
    private Class configOutputClass;

    public OperationMethod(Method method, HttpHelper parentHttpHelper) {
        this.method = method;
        if(parentHttpHelper != null) {
            this.parentTemplate = parentHttpHelper.template();
        }
        else {
            this.parentTemplate = null;
        }
        this.parentHttpHelper = parentHttpHelper;
        // build
        if(needMethodHttpHelper()) {
            try {
                this.httpHelper = buildHttpHelper();
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(),e);
            }
        }
        else {
            this.httpHelper = this.parentHttpHelper;
        }
    }

    private boolean needMethodHttpHelper(){
        if(this.parentHttpHelper == null){
            return true;
        }
        Annotation[] annotations = this.method.getDeclaredAnnotations();
        if(annotations != null){
            // Method存在以下注解,需生成新HttpHelper
            for (Annotation annotation : annotations) {
                if(annotation instanceof HttpClient
                        || annotation instanceof HttpRequest
                        || annotation instanceof Get
                        || annotation instanceof Post
                        || annotation instanceof Put
                        || annotation instanceof Delete
                ){
                    return true;
                }
            }
        }
        return false;
    }

    private HttpHelper buildHttpHelper() throws Exception {
        RequestTemplateBuilder builder = RequestTemplateBuilder.builder();
        RequestConfig requestConfig = makeRequestConfig();
        HttpClient httpClientAnn = this.method.getAnnotation(HttpClient.class);
        ClientConfig clientConfig = AnnotationUtils.makeClientConfig(httpClientAnn);
        //
        if(clientConfig == null){
            if(this.parentTemplate != null) {
                clientConfig = this.parentTemplate.getClientConfig();
            }
            else {
                clientConfig = DefaultClientConfig.okHttpClientConfig(4,3_000);
            }
        }
        builder.clientConfig(clientConfig)
                .requestConfig(requestConfig);
        return new DefaultHttpHelper(builder.build());
    }

    private RequestConfig makeRequestConfig() throws Exception {
        RequestConfig methodRequestConfig = null;
        Annotation annotation = null;
        for (Class<? extends Annotation> ann : METHOD_ANNOTATION) {
            if(this.method.isAnnotationPresent(ann)){
                annotation = this.method.getAnnotation(ann);
            }
        }
        if(annotation == null){
            for (Class<? extends Annotation> aClass : METHOD_ANNOTATION) {
                if(this.method.isAnnotationPresent(aClass)){
                    annotation = this.method.getAnnotation(aClass);
                    break;
                }
            }
        }
        Map<String, Object> requestConfigMap = AnnotationUtils.getRequestConfigMap(annotation);
        methodRequestConfig = AnnotationUtils.makeRequestConfig(requestConfigMap);
        configOutputClass = AnnotationUtils.getOutputClass(requestConfigMap);
        // 合并:优先使用method配置
        if(this.parentTemplate != null && methodRequestConfig != null){
            RequestConfig requestConfig = new RequestConfig(this.parentTemplate.getRequestConfig());
            requestConfig.mergeAll(methodRequestConfig);
            return requestConfig;
        }
        else if(methodRequestConfig != null){
            return methodRequestConfig;
        }
        else {
            return DefaultRequestConfig.getHtml();
        }
    }

    public Object execute(Object[] args){
        if(args == null || args.length <= 0){
            String name = this.method.getName();
            if(StringUtils.equals("pipeline",name)){
                if(this.parentTemplate != null) {
                    return this.parentTemplate.getRequestConfig().getHttpPipeline();
                }
                else {
                    return this.httpHelper.pipeline();
                }
            }
            else if(StringUtils.equals("template",name)){
                if(this.parentHttpHelper != null) {
                    return this.parentHttpHelper.template();
                }
                else {
                    return this.httpHelper.template();
                }
            }
        }
        String url = null;
        Object input = null;
        Consumer<ResponseFuture<Object>> callback = null;
        Class outputClass = configOutputClass;
        // 适配参数
        if(args != null) {
            for (Object arg : args) {
                if (arg instanceof String
                    && (Pattern.matches(Constant.URL_PATTERN,(String) arg) || ((String) arg).startsWith("/"))) {
                    url = (String) arg;
                } else if (arg instanceof Consumer) {
                    callback = (Consumer<ResponseFuture<Object>>) arg;
                } else if (arg instanceof Class) {
                    outputClass = (Class) arg;
                } else {
                    input = arg;
                }
            }
        }
        if(url == null){
            url = "";
        }
        ResponseFuture responseFuture = null;
        if(input != null && outputClass != null && callback != null){
            this.httpHelper.requestAsync(url,input,callback,outputClass);
        }
        else if(input != null && callback != null){
            this.httpHelper.requestAsync(url,input,callback);
        }
        else if(outputClass != null && callback != null){
            this.httpHelper.requestAsync(url,null,callback,outputClass);
        }
        else if(callback != null){
            this.httpHelper.requestAsync(url,callback);
        }
        else if(input != null && outputClass != null){
            responseFuture = this.httpHelper.request(url,input,outputClass);
        }
        else if(input != null) {
            responseFuture = this.httpHelper.request(url,input);
        }
        else if(outputClass != null){
            responseFuture = this.httpHelper.request(url,null,outputClass);
        }
        else {
            responseFuture = this.httpHelper.request(url);
        }
        if(responseFuture != null){
            Class<?> returnType = method.getReturnType();
            // 没有返回值
            if(void.class.equals(returnType)){
                return null;
            }
            // 返回ResponseFuture
            if(ResponseFuture.class.equals(returnType)){
                return responseFuture;
            }
            // 指定返回值
            if(outputClass != null && returnType.equals(outputClass)){
                return responseFuture.getOutput();
            }
            // 泛型
            Type genericReturnType = this.method.getGenericReturnType();
            if(genericReturnType != null && returnType.equals(Object.class) && outputClass != null){
                return responseFuture.getOutput();
            }
            // 其他:默认body
            if(responseFuture.isSuccess()){
                Object body = responseFuture.getResponse().getBody();
                if(returnType.equals(String.class) && body instanceof String){
                    return body;
                }
                if(returnType.equals(byte[].class) && body instanceof byte[]){
                    return body;
                }
            }
        }
        return null;
    }
}
